/**
 * Wrapper class for autocomplete plugin upon JUI autocomplete widget
 * @author Smyle
 * @requires
 * qui.autocomplete 0.0.1+
 */
(function($, undefined) {
    qui.qwidget("qui.suggest", $.qui.autocomplete, {
        widgetEventPrefix:"suggest",
        version: '0.0.1',
        /**
         * The options of suggest
         * @enum
         */
        options: {
            /** @type {String} 请求地址 */
            url: '',

            /** @type {String} 请求类型 */
            method: 'get',

            /** @type {Boolean} 是否自动选择地一条 */
            autoFocus: true,

            /** @type {Boolean} 是否可以多选 */
            multiple: true,

            /** @type {Boolean} 是否校验结果 */
            validate: true,

            /* @type {Boolean}  */
            multipleValid: false,

            /** @type {Boolean} 当validate为true时，控制onblur时是否清空无效结果 */
            clearInvalid: true,

            /** @type {String} 木结果的显示 */
            noResult: '对不起，没有这个结果',

            /** @type {Function} 一个结果时的回调函数 */
            onOneResult: null,

            /** @type {Function} 多个结果时的回调函数 */
            onMoreResult: null,

            /** @type {Function} 没有结果时的回调函数 */
            onNoResult: null,

            /** @type {Object} ajax额外参数 */
            otherParam: {},

            /** @type {Boolean} 是否添加页脚 */
            needFooter: true,

            /** @type {String} 标签属性 */
            labelKey: 'label',

            /** @type {String} 关键字属性 */
            keywordKey: 'word',

            /** @type {String} 标签id属性 */
            valueKey: 'id',

            /** @type {Boolean} 开启filter */
            autoFilter: true,

            /** @type {Boolean} 强制验证是否再autocomplete列表 */
            mustInAutoComplete: true,

            /** @type {String} tag插入顺序，"after"或"before" */
            tagInsertOrder: 'after',

            /** @type {Boolean} 强制验证tag id，若无则忽略 */
            forceTagId: true,

            /** @type {String} 默认基于行布局(row)，目前支持row, grid */
            layout: 'row',

            /** @type {Object} extendable ajax used when request is sent out */
            ajaxSettings: {

            },

            /** @type {Function} 组件初始化时调用解析函数*/
            parseValue: function() {
                var val = this.element.val(),
                    ret = [],
                    items = null,
                    itemData = null,
                    options = this.options;

                if (val) {
                    items = val.split(';');
                    $.each(
                        items,
                        function(i, item) {
                            item = item.split(',');
                            itemData = {};
                            itemData[options.labelKey] = item[1];
                            itemData[options.valueKey] = item[0];
                            ret.push(itemData);
                        }
                    );
                }
                this._value('');
                return ret;
            },

            /** @type {Function} 最终处理ajax的params */
            processParams: function(params) {
                return params;
            },

            
            /** @type {Function} callback for the 'create' event */
            create: function(event, ui) {
                $(this).suggest('init');
            },

            /**
             * @type {Function} handler to recognize raw data, the return value will
             * be used to render the menu list
             */
            process: function(json) {
                var ret = [],
                    data = $.isArray(json) ? json : json.data,
                    self = this,
                    options = self.options,
                    itemData = null;
                if (!data) {
                    return [];
                }
                $.map(data, function(item) {
                    if ('string' === typeof item) {
                        itemData = {};

                        itemData[options.labelKey]
                            = itemData[options.valueKey]
                            = itemData[self._displayKey]
                            = itemData['label']
                            = itemData['value']
                            = item;

                        ret.push(itemData);
                    } else {
                        item[self._displayKey] = item[options.labelKey];
                        item['label'] = item[options.labelKey];
                        item['value'] = item[options.labelKey];
                        ret.push(item);
                    }
                });
                return ret;
            },

            /**
             * @type {Function} handler used to highlight key word, the highlight should process
             * the label key and save it back in the storeKey, the storekey
             * is generally the displayed key defined by suggest plugin
             */
            highlight: function(data, storeKey, term) {
                var self = this,
                    options = self.options;
                data.forEach(function(item, i) {
                    var label = null;
                    if ('string' !== typeof item) {
                        key = options.labelKey;
                        label = item[key];
                        if (!label || !term) {
                            return;
                        }
                        try {
                            item[self._displayKey] = item[key].replace(new RegExp('(' + term + ')', 'ig'), '<b>$1</b>');
                        } catch (ex) {}
                    } else {
                        data[i] = item.replace(new RegExp('(' + term + ')', 'ig'), '<b>$1</b>');
                    }
                });
            }
        },

        _renderMenu: function(ul, items){
            // set the style of UL according to the param 'layout'
            switch (this.options.layout) {
            case 'grid':
                ul.addClass('qui-grid-celllayout');
                break;
            }
            this._super(ul, items);
        },

        /**
         * return an array of processed items which is used to render the suggest menu list
         * @return {Array}
         */
        getResultData: function() {
            return this.result;
        },

        /* send ajax request to remote suggest service */
        _getSuggestData: function(url, method, params, callback) {
            $.ajax( $.extend(
                {
                    url: url,
                    dataType: 'json',
                    data: this.options.processParams ? this.options.processParams(params) : params,
                    cache: false,
                    method: method,
                    success: function(data) {
                        if (callback) {
                            callback.call(this, data);
                        }
                    },
                    error: function(data) {
                        if (callback) {
                            callback.call(this, data);
                        }
                    }
                }, this.options.ajaxSettings || {}
            ));
        },

        _handleResponse: function(request, response, data) {
            /*
             * entrance for suggest to handle the request process
             * @param {Object} requset JSON in format of { term: value }
             * @param {Function} response the function to handle the response of request
             * @param {Object} data raw array used to render the suggest list
             */
            var self = this,
                options = self.options,
                term = $.trim(request.term),
                dataProcess = options.process,
                ret = dataProcess.call(self, data, term);

            ret = options.autoFilter ? $.ui.autocomplete.filter(ret, term) : ret;

            if (options.highlight) {
                options.highlight.call(self, ret, self._displayKey, term);
            }

            self.result = ret;
            response(ret);
        },

        _initSource: function() {
            /**
             * override original JUI autocomplete source initializer
             */
            var array, self = this;
            if ($.isArray(self.options.source)) {
                array = self.options.source;
                self.source = function(request, response) {
                    self._handleResponse(request, response, array);
                };
            } else if (typeof this.options.source === "string") {
                self.options.url = url = self.options.source;
                self.source = self.__source;
            } else {
                self.source = self.options.source;
            }
        },

        __source: function(request, response) {
            /**
             * invoked when there is a need to send an ajax suggest
             * @param {Object} request JSON in format of { term: value }
             * @param {Function} response function to handle the response of request
             */
            var self = this,
                options = self.options,
                term = $.trim(request.term),
                url = null,
                getParam = null,
                param = {};

            if (!term) {
                return null;
            }

            if (!options.ignoreWord) {
                //传递 word  term
                param[options.keywordKey ? options.keywordKey : 'word'] = term;
            }

            param = $.extend(true, options.otherParam, param);

            url = options.url;
            getParam = options.getParam;
            if (getParam) {
                if ($.isFunction(options.getParam)) {
                    param = $.extend(true, param, getParam.call(self, term));
                } else if ($.isPlainObject(getParam)) {
                    param = $.extend(true, param, getParam);
                }
            }

            self._getSuggestData(url, options.method, param, $.proxy(self._handleResponse, self, request, response));
        },

        _create: function() {
            var $this = this.element;
            $this.wrap('<span class="ui-front ui-suggest"></span>');
            this._super();
            $this.on('suggestresponse', function(event, ui) {
                var content = ui.content;
                $this.suggest('validate', content);
            }).on('suggestselect', function(event, ui) {
                return $this.suggest('setValue', ui.item);
            }).on('suggestsearch', function(event, ui) {
                return $this.suggest('multiplesearch');
            });
        },

        /*为了兼容Firefox和Chrome的在中文输入法下Suggest的bug，还原jquery的该函数为1.10.2版本*/
        _searchTimeout: function( event ) {
            clearTimeout( this.searching );
            this.searching = this._delay(function() {
                // only search if the value has changed
                if ( this.term !== this._value() ) {
                    this.selectedItem = null;
                    this.search( null, event );
                }
            }, this.options.delay );
        }
    });
}(jQuery));